#include "waylanwindowmanager.h"

#include <KWayland/Client/connection_thread.h>
#include <KWayland/Client/registry.h>
#include <KWayland/Client/event_queue.h>

#include "waylandwindow.h"

#include <QDebug>
#include <QApplication>
#include <QGuiApplication>

#include <wayland-client.h>
#include <wayland-client-protocol.h>

#define GET_DESKTOP_EXEC_NAME_MAIN          "cat %s | awk '{if($1~\"Exec=\")if($2~\"\%\"){print $1} else print}' | cut -d '=' -f 2"
#define GET_DESKTOP_EXEC_NAME_BACK          "cat %s | awk '{if($1~\"StartupWMClass=\")print $1}' | cut -d '=' -f 2"
#define GET_DESKTOP_ICON                    "cat %s | awk '{if($1~\"Icon=\")print $1}' | cut -d '=' -f 2"
#define GET_PROCESS_EXEC_NAME_MAIN          "ps -aux | sed 's/ \\+/ /g' |awk '{if($2~\"%d\")print}'| cut -d ' ' -f 11-"

#define TEST1

WaylanWindowManager::WaylanWindowManager(UKUIWindowManager *parent) : UKUIWindowManager(parent)
{
    init();
}

WaylanWindowManager::~WaylanWindowManager()
{
}

void WaylanWindowManager::addWindow(KWayland::Client::PlasmaWindow *window)
{
    UKUIWindow* newWindow = new WaylandWindow(window);
    m_windows.append(newWindow);

    auto removeWindow = [newWindow, this] {
        const int index = m_windows.indexOf(newWindow);
        if(index != -1) {
            m_windows.removeOne(newWindow);
            emit windowRemoved(newWindow);
            newWindow->deleteLater();
        }
    };


    //connect(window, &KWayland::Client::PlasmaWindow::unmapped, this, &WaylanWindowManager::removeWindow);
    //connect(window, &QObject::destroyed, this, &WaylanWindowManager::removeWindow);
    connect(window, &KWayland::Client::PlasmaWindow::unmapped, this, removeWindow);
    connect(window, &QObject::destroyed, this, removeWindow);


    connect(window, &KWayland::Client::PlasmaWindow::closeableChanged, this, &WaylanWindowManager::slotCloseableChanged);


    emit windowAdded(newWindow);
}

//void WaylanWindowManager::removeWindow()
//{
//    UKUIWindow* window = dynamic_cast<UKUIWindow*>(sender());
//    showWindows();
//    if(window) {
//        m_windows.removeOne(window);
//    }
//    emit windowRemoved(window);
//    window->deleteLater();
//}

void WaylanWindowManager::slotCloseableChanged()
{
    qDebug()<<"closeable changed";
}


void WaylanWindowManager::init()
{
    //todo
    //add function to check if current environment is wayland

    qDebug()<<"init wayland connection";
#ifdef TEST1
    auto _display = wl_display_connect(NULL);
    m_display = _display;
    if (!m_display) {
        qWarning()<<"can not connect wayland display";
        return;
    }
    int fd = wl_display_get_fd(_display);
    m_fd = fd;
    if (fd == -1) {
        qWarning()<<"can not connect wayland display fd";
        return;
    }

    m_eventQueue = new KWayland::Client::EventQueue(this);
    m_eventQueue->setup(_display);

    m_registry = new KWayland::Client::Registry(this);
    m_registry->create(_display);
    m_registry->setEventQueue(m_eventQueue);

    if (!m_registry) {
        qCritical()<<"registry is null";
        return;
    }

    m_socketNotifier = new QSocketNotifier(m_fd, QSocketNotifier::Read, this);
    connect(m_socketNotifier, &QSocketNotifier::activated, [=](){
        wl_display_flush(m_display);
        if (wl_display_dispatch(m_display) == -1) {
            auto error = wl_display_get_error(m_display);
            if (error != 0) {
                if (m_display) {
                    free(m_display);
                }
                qCritical()<<"wayland display went error, exit the program";
                return exit(error);
            }
        }
        m_eventQueue->dispatch();
    });

#else


    //qDebug()<<qApp->applicationDirPath()<<qApp->applicationDisplayName();
    auto connection = KWayland::Client::ConnectionThread::fromApplication (this);
    if(!connection) {
        qWarning()<<"Wayland init failed: can not get ConnectionThread from Application";
        return;
    }

    m_registry = new KWayland::Client::Registry(this);
    m_registry->create(connection);

#endif

    connect(m_registry, &KWayland::Client::Registry::plasmaWindowManagementAnnounced, [this] (quint32 name, quint32 version) {
        qDebug()<<"plasma window management announced";
        windowManagement = m_registry->createPlasmaWindowManagement(name, version);


        connect(windowManagement, &KWayland::Client::PlasmaWindowManagement::interfaceAboutToBeReleased, [this] {
            //            q->beginResetModel();
            //            windows.clear();
            //            q->endResetModel();
            m_windows.clear();
        });

        connect(windowManagement, &KWayland::Client::PlasmaWindowManagement::windowCreated, [this](KWayland::Client::PlasmaWindow *window) {

            qDebug()<<window->appId();//
            qDebug()<<window->applicationMenuObjectPath();
            qDebug()<<window->applicationMenuServiceName();
            qDebug()<<window->pid();
            qDebug()<<window->title();
            qDebug()<<"+++++++++++++++++++++++++++++++++++++++++++++++++";

            addWindow(window);
        });

        const auto windows = windowManagement->windows();
        for (auto it = windows.constBegin(); it != windows.constEnd(); ++it) {
            addWindow(*it);
        }

#ifdef TEST1
        wl_display_roundtrip(m_display);
        m_eventQueue->dispatch();
#endif
    });

    m_registry->setup();

#ifdef TEST1
    wl_display_roundtrip(m_display);
    m_eventQueue->dispatch();
#endif
}

void WaylanWindowManager::showWindows()
{
    qDebug()<<"++++++++++++++++++++++++++++++++++++++++++++++++++++++";
    qDebug()<<".........show windows...............";
    foreach(UKUIWindow* window, m_windows) {
        if(window)
            qDebug()<<window->getAppName()<<window->getTitle();
        else
            qDebug()<<"---------------window is null--------------------";
    }
    qDebug()<<"++++++++++++++++++++++++++++++++++++++++++++++++++++++";
}

